#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <cassert>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/socket.h>

// #define BUFFER_NUM 1024

const int buffer_num = 1024;

std::string default_ip = "0.0.0.0";

enum {
    SOCKET_ERR = 1,
    BIND_ERR,
    USAGE_ERR,
    RECVFROM_ERR,
    OPEN_ERR,
    SENDTO_ERR
};

class UdpServer
{
public:
    using func_t = std::function<void(int, std::string, uint16_t, std::string)>;
public:
    UdpServer(func_t func, uint16_t serverport, std::string serverip = default_ip):_func(func), _serverport(serverport)
    {}
    void init()
    {
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_socketfd == -1)
        {
            std::cerr << errno << ": "<< strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }
        struct sockaddr_in local;
        bzero(&local, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_serverport);
        local.sin_addr.s_addr = INADDR_ANY;
        //绑定的是服务器本地的端口,需要发送给网络，让客户端知道是哪个端口，因此这里需要用htons
        //就像快递单一样
        // si.sin_addr.s_addr = inet_addr(_serverip.c_str());
        // 不建议这样写，因为服务器可能不止有有一个ip，假如绑定了一个唯一的ip，那么发送给别的ip的数据将无法向上交付
        // 或者是 si.sin_addr.s_addr = INADDR_ANY
        int n = bind(_socketfd, (struct sockaddr*)&local, sizeof(local));
        if(n == -1)
        {
            std::cerr << errno << ": "<< strerror(errno) << std::endl;
            exit(BIND_ERR);
        }
        (void)n;
    }
    void start()
    {
        //log:
        // std::cout << "服务端启动" << std::endl;
        for(;;)//服务端正常是死循环式进行运行的
        {
            char buffer[buffer_num];
            std::string message;
            struct sockaddr_in client;
            socklen_t clientlen = sizeof(client);
            int n = recvfrom(_socketfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&client, &clientlen);
            //该函数会进行阻塞式等待消息
            if(n == -1)
            {
                std::cerr << errno << ": "<< strerror(errno) << std::endl;
                exit(RECVFROM_ERR);
            }
            buffer[n] = 0;
            message = buffer;
            std::string clientip = inet_ntoa(client.sin_addr);
            uint16_t clientport = ntohs(client.sin_port);
            std::cout << clientip << "[" << clientport << "]# " << message << std::endl;
            _func(_socketfd, clientip, clientport, message);
        }
    }
    ~UdpServer()
    {}
private:
    std::string _serverip;//点分十进制
    uint16_t _serverport;//服务端的端口
    int _socketfd;//打开的文件描述符
    func_t _func;//对传入的数据进行处理的函数
};